package character;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import static org.junit.Assert.*;

import org.junit.Test;


public class NFTest {

	@Test
	
	/**Tested a basic cycle and creation of NFA**/
	public void basicTest(){
		State state0 = new State(0);
		State state1 = new State(1);
		State state2 = new State(2);
	
		Input forState0 = new Input("a", state2);
		state0.addInput(forState0);
		
		NFA newNFA = new NFA();
		newNFA.addState(state0);
		
		List<State> states = newNFA.getStates();
		assertEquals(1, states.size());
		
		State potentialState0 = states.get(0);
		assertEquals(potentialState0.getInputs().size(), 1);
		
		List<Input> inputs = potentialState0.getInputs();
		Input input0 = inputs.get(0);
		
		State backTo0 = input0.getNextState();
		
		assertEquals(backTo0, state2);
		
	}

	/**Let's test the merging**/

	@Test
	public void mergeStarTest(){
		State state0 = new State(0);
		State state1 = new State(1);
		State state2 = new State(2);
		State state3 = new State(3);
		
		state0.setStartState(true);
		state1.setFinishState(true);
		state2.setFinishState(true);

		Input input0 = new Input("1", state1);
		Input input1 = new Input("1", state2);
		Input starInput = new Input("2", state0);
		
		
		state0.addInput(input0);
		state1.addInput(input1);
	
		
		NFA NFA1 = new NFA();
		NFA NFA2 = new NFA();
		
		List<State> statesNFA1 = new ArrayList();
		List<State> statesNFA2 = new ArrayList();
		
		statesNFA1.add(state0);
		statesNFA1.add(state1);
		statesNFA1.add(state2);
		
		NFA1.addState(state0);
		NFA1.addState(state1);
		NFA1.addState(state2);
		NFA2.addState(state2);
		
//		NFA2.addState(state1);
		
		NFA1.mergeStar(NFA1);
		
		
//MERGE STAR TEST.
		assertEquals(state1, NFA1.getState(1));
		
		State finState = NFA1.getState(1);
		
		List<Input> finInputs = finState.getInputs();
		Boolean check=false;
		assertEquals(2, finInputs.size());
		for(int k=0; k<finInputs.size(); k++){
			Input input = finInputs.get(k);
			if(input.getNextState().getID()==0){
				check=true;
			}
			
		}
		assertTrue(check);
		
		
	}
	
	@Test
	//MERGE PLUS
	public void mergePlusTest(){
		State state0 = new State(0);
		State state1 = new State(1);
		State state2 = new State(2);
		State state3 = new State(3);
		
		state0.setStartState(true);
		state3.setFinishState(true);

		Input input0 = new Input("1", state1);
		Input input1 = new Input("1", state2);
		Input input2 = new Input("1", state3);
		
		Input starInput = new Input("2", state0);
		
		
		state0.addInput(input0);
		state0.addInput(input2);
		state1.addInput(input1);
		state2.addInput(input2);
		
		NFA NFA1 = new NFA();
		NFA1.addState(state0);
		NFA1.addState(state1);
		NFA1.addState(state2);
		NFA1.addState(state3);
		
		assertEquals(state3, NFA1.getState(3));
		
		List<State> theStates = NFA1.getStates();
		int x =-1;
		for (int k=0; k<theStates.size(); k++){
			State getState= theStates.get(k);
			if(getState.isFinishState()){
				x = getState.getID();
			}
		}
		assertEquals(3, x);
		
		NFA1 = NFA1.mergePlus(NFA1);
		
		State finState= NFA1.getState(3);
		
		List<Input> finInputs = finState.getInputs();
		Boolean check=false;
		for(int k=0; k<finInputs.size(); k++){
			Input input = finInputs.get(k);
			if(input.getNextState().equals(state0)){
				check=true;
			}
		}
		assertTrue(check);
	}
	
	@Test
	public void mergeUnionTest(){
		State m0 = new State(0);
		State m1 = new State(1);
		State m2 = new State(2);
		State m3 = new State(3);
		State m4 = new State(4);
		
		State n0 = new State(0);
		State n1 = new State(1);
		State n2 = new State(2);
		State n3 = new State(3);
		State n4 = new State(4);
		
		Input mi0 = new Input("a", m0);
		Input mi01 = new Input("b", m0);
		Input mi1 = new Input("d", m1);
		
		m0.setStartState(true);
		n0.setStartState(true);
		
		m0.addInput(mi0);
		m0.addInput(mi1);
		m0.addInput(mi01);
		
		NFA NFA1 = new NFA();
		NFA NFA2 = new NFA();
		
		NFA1.addState(m0);
		NFA1.addState(m1);
		
		NFA2.addState(n0);
		
		NFA1 = NFA1.mergeUnion(NFA1, NFA2);
		
		assertFalse(n0.isStartState());
		
		
		List<Input> startInputs = m0.getInputs();
		assertEquals(4, startInputs.size());
		Input input = new Input("Epsilon", n0);
		Boolean check=false;
		for(int k=0; k<startInputs.size(); k++){
			Input testInput = startInputs.get(k);
			if(testInput.getNextState().equals(n0)){
				check = true;
			}
		}
		assertTrue(check);
		
		check =false;
		for(int k=0; k<NFA1.getStates().size(); k++){
			List<State> stateList = NFA1.getStates();
			for(int c=0; c<stateList.size(); c++){
				State stater = stateList.get(c);
				if(stater.getID()==1){
					check=true;
				}
			}
		}
		assertTrue(check);
	}
	
	@Test
	public void mergeConcatTest(){
		State stateNFA0 = new State(0);
		State stateNFA1 = new State(0);
		
		stateNFA0.setStartState(true);
		stateNFA0.setFinishState(true);
		
		stateNFA1.setFinishState(true);
		
		NFA NFA1 = new NFA();
		NFA NFA2 = new NFA();
		
		NFA1.addState(stateNFA0);
		NFA2.addState(stateNFA1);
		
		NFA1 = NFA1.mergeConcat(NFA1, NFA2);
		
		assertEquals(2, NFA1.getStates().size());
	}
	
	@Test
	public void NFAtoDFATest(){
		State m0 = new State(0);
		State m1 = new State(1);
		State m2 = new State(2);
		State m3 = new State(3);
		State m4 = new State(4);
		
		State n0 = new State(0);
		State n1 = new State(1);
		State n2 = new State(2);
		State n3 = new State(3);
		State n4 = new State(4);
		
		Input mi0 = new Input("a", m0);
		Input mi01 = new Input("b", m0);
		Input mi1 = new Input("d", m1);
		
		m0.setStartState(true);
		n0.setStartState(true);
		m1.setFinishState(true);
		
		m0.addInput(mi0);
		m0.addInput(mi1);
		m0.addInput(mi01);
		
		NFA NFA1 = new NFA();
		NFA1.addState(m0);
		NFA1.addState(m1);
		
		DFA newDFA = NFA1.toDFA();
		
		if(newDFA.getStates().size()<NFA1.getStates().size()){
			
		}
	}
	
	@Test
	public void anotherNtoDTest(){
		State state0 = new State(0);
		State state1 = new State(1);
		State state2 = new State(2);
		
		Input input0 = new Input("a", state1);
		Input input1 = new Input("b", state1);
		Input input2 = new Input("b", state2);
		Input input3 = new Input("a", state0);
		
		state0.addInput(input0);
		state0.addInput(input1);
		state1.addInput(input2);
		state2.addInput(input3);
		
		state0.setStartState(true);
		state2.setFinishState(true);
		
		NFA NFA1 = new NFA();
		
		NFA1.addState(state0);
		NFA1.addState(state1);
		NFA1.addState(state2);
		
		DFA newDFA = NFA1.toDFA();
		
		System.out.println("DFA size: " + newDFA.getStates().size());
		
		List<State> Dins = newDFA.getStates();
		List<Input> ins = Dins.get(0).getInputs();
		
		for(int k=0; k<ins.size(); k++){
			System.out.println("Title: " + ins.get(k).getTitle());
		}
		
		for(int k=0; k<Dins.size(); k++){
			if(Dins.get(k).isFinishState()){
			}
		}
		assertTrue(Dins.get(1).isFinishState());
		
	}
}
